package com.lingyuan.oms.listener;

import com.alibaba.fastjson.JSON;
import com.lingyuan.common.core.domain.R;
import com.lingyuan.common.core.domain.vo.call.WareCall;
import com.lingyuan.common.core.domain.vo.call.WmsConstant;
import com.lingyuan.common.core.domain.vo.cart.CartItemVo;
import com.lingyuan.common.core.domain.vo.pms.SpuOrderItemVo;
import com.lingyuan.common.core.domain.vo.sms.SkuCouponPrice;
import com.lingyuan.common.core.exception.ServiceException;
import com.lingyuan.oms.constant.OrderConstant;
import com.lingyuan.oms.constant.OrderStatusEnum;
import com.lingyuan.oms.constant.RabbitMqConstant;
import com.lingyuan.oms.domain.OrderEntity;
import com.lingyuan.oms.domain.OrderItemEntity;
import com.lingyuan.oms.domain.param.CreateOrderParam;
import com.lingyuan.oms.remote.ProductRemoteService;
import com.lingyuan.oms.remote.WarehouseRemoteService;
import com.lingyuan.oms.service.OrderItemService;
import com.lingyuan.oms.service.OrderService;
import com.rabbitmq.client.Channel;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @ClassName：PaymentOrderListener
 * @Author: LeiHao
 * @Date: 2024/8/29 16:54
 * @Description: 15分钟后订单未支付自动取消
 */
@Component
@Log4j2
public class PaymentOrderListener {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private OrderService orderService;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private ProductRemoteService productRemoteService;
    @Autowired
    private WarehouseRemoteService warehouseRemoteService;
    @Autowired
    private ThreadPoolTaskExecutor  orderThreadPool;

    @Transactional(rollbackFor = Exception.class)
    @RabbitListener(queues = RabbitMqConstant.QUEUE_PAYMENT_RELEASE_NAME)
    public void paymentOrderListener(String orderSn, Channel channel, Message message) {
        try {
            if (stringRedisTemplate.hasKey(RabbitMqConstant.PAYMENT_ORDER_MESSAGE_KEY + message.getMessageProperties().getMessageId())) {
                //确认消费
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
            log.info("接受信息：{}", orderSn);
            String orderStr = stringRedisTemplate.opsForValue().get(OrderConstant.SUBMIT_ORDER_KEY + orderSn);
            OrderEntity orderEntity = JSON.parseObject(orderStr, OrderEntity.class);
            //用户在15分钟内完成支付订单
            if (orderEntity.getStatus() == OrderStatusEnum.PAID.getCode()) {
                String cartItemListStr = stringRedisTemplate.opsForValue().get(OrderConstant.ORDER_CART_LIST_KEY + orderEntity.getOrderSn());
                List<CartItemVo> cartItemVoList = JSON.parseArray(cartItemListStr, CartItemVo.class);
                // 异步扣减库存
                orderThreadPool.execute(() -> subRedisStock(cartItemVoList));
                // 减库存+添加订单明细
                List<SkuCouponPrice> skuCouponPriceList = null;
                if (orderEntity.getCouponId() != null) {
                    String skuCouponPriceListStr = stringRedisTemplate.opsForValue().get(OrderConstant.ORDER_COUPON_KEY + orderEntity.getOrderSn());
                    skuCouponPriceList = JSON.parseArray(skuCouponPriceListStr, SkuCouponPrice.class);
                }
                // 异步添加订单明细
                List<SkuCouponPrice> finalSkuCouponPriceList = skuCouponPriceList;
                orderThreadPool.execute(() -> addOrderItem(cartItemVoList, orderEntity, finalSkuCouponPriceList));
            }
            if (orderEntity.getStatus() == OrderStatusEnum.UNPAID.getCode()) {
                //TODO 关闭订单
            }
            //防止重复消费
            stringRedisTemplate.opsForValue().set(RabbitMqConstant.PAYMENT_ORDER_MESSAGE_KEY + message.getMessageProperties().getMessageId(), orderStr, 6, TimeUnit.HOURS);
            //确认消费
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            e.printStackTrace();
            try {
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }


    //扣减redis库存,锁定数据库库存
    private void subRedisStock(List<CartItemVo> cartItemVoList) {
        String script = "local result = {}\n" +
                "for i=1,#ARGV,2 do\n" +
                "local productId = ARGV[i]\n" +
                "local payNum = tonumber(ARGV[i + 1])\n" +
                "local currentStock = tonumber(redis.call('hget', KEYS[1], productId))\n" +
                "if not currentStock or currentStock < payNum then\n" +
                "table.insert(result, {productId, '库存不足'})\n" +
                "else\n" +
                "redis.call('hincrby', KEYS[1], productId, -payNum)\n" +
                "end\n" +
                "end\n" +
                "return result";

        List<String> keys = Collections.singletonList(WmsConstant.WARE_LOCK_KEY_VALUE);
        List<String> args = new ArrayList<>();

        cartItemVoList.forEach(cartItemVo -> {
            args.add(cartItemVo.getProductId().toString()); // 商品 ID
            args.add(String.valueOf(cartItemVo.getPayNum())); // 需要扣减的数量
        });

        List<Object> response = stringRedisTemplate.execute(new DefaultRedisScript<>(script, List.class), keys, args.toArray());

        // 检查是否有任何库存不足的情况
        boolean allSuccess = response.stream()
                .flatMap(item -> item instanceof List ? ((List<?>) item).stream() : Stream.of(item))
                .noneMatch(item -> item instanceof List && !((List<?>) item).isEmpty());

        if (allSuccess) {
            // 同步数据库锁定库存
            List<WareCall> wareCallList = cartItemVoList.stream().map(cartItemVo -> {
                WareCall wareCall = new WareCall();
                wareCall.setId(cartItemVo.getProductId());
                wareCall.setStock(Long.valueOf(cartItemVo.getPayNum()));
                return wareCall;
            }).collect(Collectors.toList());
            warehouseRemoteService.lockStock(wareCallList);
            log.info("所有商品的库存扣减操作均成功！");
        } else {
            log.info("部分商品的库存扣减失败，请检查以下信息：");
            response.stream()
                    .flatMap(item -> item instanceof List ? ((List<?>) item).stream() : Stream.of(item))
                    .filter(item -> item instanceof List && !((List<?>) item).isEmpty())
                    .forEach(item -> {
                        List<?> resList = (List<?>) item;
                        String productId = (String) resList.get(0);
                        String message = (String) resList.get(1);
                        log.info("商品ID：{}, 错误信息：{}", productId, message);
                    });
        }
    }

    /* *
     * @Title: addOrderItem
     * @Author: DELL
     * @Date: 2024-09-25 09:26:38
     * @Params: [cartItemVoList, orderEntity, orderSn, skuCouponPriceList]
     * @Return: void
     * @Description: 添加订单详情
     */
    private void addOrderItem(List<CartItemVo> cartItemVoList, OrderEntity orderEntity, List<SkuCouponPrice> skuCouponPriceList) {
        List<OrderItemEntity> orderItemEntityList = cartItemVoList.stream().map(c -> {

            OrderItemEntity orderItem = new OrderItemEntity();
            R<SpuOrderItemVo> bySkuIdSpuInfo = productRemoteService.findBySkuIdSpuInfo(c.getProductId());
            if (bySkuIdSpuInfo.getCode() != 200) {
                throw new ServiceException("获取商品:" + c.getProductId() + "信息失败");
            }
            SpuOrderItemVo spuOrderItemVo = bySkuIdSpuInfo.getData();
            BeanUtils.copyProperties(spuOrderItemVo, orderItem);
            orderItem.setOrderId(orderEntity.getId());
            orderItem.setOrderSn(orderEntity.getOrderSn());
            orderItem.setSkuId(c.getProductId());
            orderItem.setSkuName(c.getProductName());
            orderItem.setSkuPic(c.getProductImg());
            orderItem.setSkuPrice(c.getTotalPrice());
            orderItem.setSkuQuantity(Long.valueOf(c.getPayNum()));
            orderItem.setSkuAttrsVals(JSON.toJSONString(c.getAttrs()));
            if (!CollectionUtils.isEmpty(skuCouponPriceList)) {
                skuCouponPriceList.stream().forEach(s -> {
                    if (s.getSkuId().equals(c.getProductId())) {
                        orderItem.setCouponAmount(s.getCouponPrice());
                        orderItem.setRealAmount(s.getTotalPrice());
                    }
                });
            }
            orderItem.setCreateTime(LocalDateTime.now());
            return orderItem;

        }).collect(Collectors.toList());
        orderItemService.saveBatch(orderItemEntityList);
    }
}
